﻿#include <Sys\RenderUnit\SysRenderDatagram\LIGHTSOURCERD.HPP>
#include <Render\Render_Win32\Render_Win32_D3D\RENDER_WIN32_D3D9.HPP>
#include <Sys\RenderUnit\SYSDDCLASSID.HPP>
#include <Sys\MATH.HPP>
#include <d3dx9.h>

using namespace CxxlMan;
using namespace Cxxl3D;

class CLightSourceMgrDD_D3D9:public IDatagramDisposal_D3d9,public ILightSourceVisitor, public ILightSourceMgrBridge
{
  // 記錄和光源所配對的 LightID
  struct tagLightSourcMap:public cxxlObject
  {
    unsigned int LightID;
    Smart_Ptr<ILightSource> ILightSource_Ptr; // 保存作為 Key 的光源，以確保 Key 一定存在

    // Constructor
    tagLightSourcMap(unsigned int ID, const Smart_Ptr<ILightSource> &ILightSource_Arg)
      :cxxlObject(Spirit_Easy),
       ILightSource_Ptr(ILightSource_Arg,this)
    {
      LightID = ID;
    }
  };

  class LightSourcMapTree:public cxxlAVL_Tree<tagLightSourcMap,ILightSource*,true>
  {
    // 排序檢測，延伸類別須實作此函數
    // 要讓 O_Key 鍵值所代表的物件排在 左 回答 true，否則 false
    bool cxxlFASTCALL isLeftAt(ILightSource* O_Key,ILightSource* addKey) const
    {
      return O_Key <= addKey;
    } 
   
    // 這函數用來決定在容器中 O_Key 鍵值所代表的物件，是不是 findKey 所要尋找的 
    // 是 回答 true，否則 false，延伸類別須實作此函數
    bool cxxlFASTCALL isEqual(ILightSource* O_Key,ILightSource* findKey)  const
    {
      return O_Key == findKey;
    } 
   
  public:
    LightSourcMapTree(ISpirit *spirit = Spirit_Easy)  // Constructor
     :cxxlObject(spirit)
    {
    }
  }TakeTree; // 已使用的 tagLightSourcMap

  cxxlList<tagLightSourcMap> WaitingList; // 可用的 tagLightSourcMap


  // 移除光源
  void cxxlFASTCALL DelLightSource(const Smart_Ptr<cxxlList<ILightSource> > &DelILightSource_List)
  {
    DelILightSource_List->ResetPT(toHead);
    unsigned long Count = DelILightSource_List->GetCount();
    unsigned long n = 0;
    for(Smart_Ptr<ILightSource> ILightSource_Ptr = (*DelILightSource_List)++; n < Count; ILightSource_Ptr = (*DelILightSource_List)++,++n)
    {
      Smart_Ptr<tagLightSourcMap> tagLightSourcMap_Ptr = TakeTree.Pump(ILightSource_Ptr);			
      m_pDEVICE9->LightEnable(tagLightSourcMap_Ptr->LightID, FALSE); // 關閉這盞光
      tagLightSourcMap_Ptr->ILightSource_Ptr.Destroy();
      WaitingList.Push_Back(tagLightSourcMap_Ptr);
    }
  }

  // 加入光源
  void cxxlFASTCALL AddLightSource(const Smart_Ptr<cxxlList<ILightSource> > &NewILightSource_List)
  {
    NewILightSource_List->ResetPT(toHead);
    unsigned long Count = NewILightSource_List->GetCount();
    unsigned long n = 0;
    for(Smart_Ptr<ILightSource> ILightSource_Ptr = (*NewILightSource_List)++; n < Count; ILightSource_Ptr = (*NewILightSource_List)++,++n)
    {
      Smart_Ptr<tagLightSourcMap> tagLightSourcMap_Ptr = WaitingList.Pop_Back();
      tagLightSourcMap_Ptr->ILightSource_Ptr = ILightSource_Ptr;
      TakeTree.Add(tagLightSourcMap_Ptr,ILightSource_Ptr);

      LightID = tagLightSourcMap_Ptr->LightID;
      m_pDEVICE9->LightEnable(LightID, TRUE); // 打開這盞光
      ILightSource_Ptr->Accept(*this);
    }
  }


  Smart_Ptr<ILightAmbient> m_ILightAmbient_Ptr;
  Smart_Ptr<ILightSourceMgrRD> m_ILightSourceMgrRD_Ptr;
  Smart_Ptr<cxxlList<ILightSource> > m_ILightSource_List;



  DWORD LightID; // 
  LPDIRECT3DDEVICE9 m_pDEVICE9;

  virtual void cxxlFASTCALL VisitLightDirectional(const Smart_Ptr<ILightDirectional> &ILightDirectional_Arg)
  {
    Smart_Ptr<IC3D_Node> IC3D_Node_Ptr = ILightDirectional_Arg->Get_IC3D_Node();
    CSmart_Ptr<const Matrix4x4> WorldMatrix_cPtr = IC3D_Node_Ptr->GetWorldMatrix();
    m_pDEVICE9->SetTransform(D3DTS_WORLD, (D3DXMATRIX*)(const Matrix4x4*)WorldMatrix_cPtr); // Set world matrix


    C3DVector4 Specular, Ambient, Color, Directional;

    ILightDirectional_Arg->GetData(Specular, Ambient, Color, Directional);

    // D3D9 經由設定 D3DLIGHT9 struct 來設定光源屬性
    D3DLIGHT9 light;
    ZeroMemory( &light, sizeof(light) );

    // 設定成方向光
    light.Type = D3DLIGHT_DIRECTIONAL;

    // 設定方向
    light.Direction = *(D3DVECTOR *) &Directional;

    // 光源的 Specular 值
    light.Specular = *(D3DCOLORVALUE*) &Specular;

    // 光源的 Ambient 值
    light.Ambient = *(D3DCOLORVALUE*) &Ambient;

		// 光源的Diffuse值
    light.Diffuse = *(D3DCOLORVALUE*) &Color;

    // 光源有效範圍, 在此把它設定成浮點數所容計的最大值
    light.Range = 1000.0f;

    // 用SetLight來設定光源屬性
		m_pDEVICE9->SetLight(LightID, &light);
  }

  virtual void cxxlFASTCALL VisitLightPoint(const CxxlMan::Smart_Ptr<ILightPoint> &ILightPoint_Arg)
  {
    Smart_Ptr<IC3D_Node> IC3D_Node_Ptr = ILightPoint_Arg->Get_IC3D_Node();
    CSmart_Ptr<const Matrix4x4> WorldMatrix_cPtr = IC3D_Node_Ptr->GetWorldMatrix();
    m_pDEVICE9->SetTransform(D3DTS_WORLD, (D3DXMATRIX*)(const Matrix4x4*)WorldMatrix_cPtr); // Set world matrix


    C3DVector4 Specular, Ambient, Color, Position, Attenuation;

    ILightPoint_Arg->GetData(Specular, Ambient, Color, Position, Attenuation);

    // D3D9 經由設定 D3DLIGHT9 struct 來設定光源屬性
    D3DLIGHT9 light;
    ZeroMemory( &light, sizeof(light) );

    // 設定成點光源
    light.Type = D3DLIGHT_POINT;

    // 設定位置
    light.Position = *(D3DVECTOR *) &Position;

    // 隨距離的衰減值
    // 1/(CONSTANT+LINEAR*d+QUADRATIC*d^2)
    // 公式中的 CONSTANT、LINEAR、QUADRATIC 值
    light.Attenuation0 = Attenuation[0];
    light.Attenuation1 = Attenuation[1];
    light.Attenuation2 = Attenuation[2];

    // 光源的 Specular 值
    light.Specular = *(D3DCOLORVALUE*) &Specular;

    // 光源的Ambient值
    light.Ambient = *(D3DCOLORVALUE*) &Ambient;

    // 光源的Diffuse值
    light.Diffuse = *(D3DCOLORVALUE*) &Color;

    // 光源有效範圍, 在此把它設定成浮點數所容計的最大值
    light.Range = 1000.0f;


    // 用SetLight來設定光源屬性
		m_pDEVICE9->SetLight(LightID, &light);
  }

  virtual void cxxlFASTCALL VisitLightSpot(const CxxlMan::Smart_Ptr<ILightSpot> &ILightSpot_Arg)
  {
    Smart_Ptr<IC3D_Node> IC3D_Node_Ptr = ILightSpot_Arg->Get_IC3D_Node();
    CSmart_Ptr<const Matrix4x4> WorldMatrix_cPtr = IC3D_Node_Ptr->GetWorldMatrix();
    m_pDEVICE9->SetTransform(D3DTS_WORLD, (D3DXMATRIX*)(const Matrix4x4*)WorldMatrix_cPtr); // Set world matrix


    C3DVector4 Specular, Ambient, Color, Directional, Position, Attenuation;
    C3D_FLOAT SpotlightCutoff, SpotlightExponent;

    ILightSpot_Arg->GetData(Specular, Ambient, Color, Directional, Position, Attenuation, SpotlightCutoff, SpotlightExponent);

    // D3D9 經由設定 D3DLIGHT9 struct 來設定光源屬性
    D3DLIGHT9 light;
    ZeroMemory( &light, sizeof(light) );

    // 設定成聚光燈
    light.Type = D3DLIGHT_SPOT;

    // 設定位置
    light.Position = *(D3DVECTOR *) &Position;

    // 設定方向
    light.Direction = *(D3DVECTOR *) &Directional;

    // 隨距離的衰減值
    // 1/(CONSTANT+LINEAR*d+QUADRATIC*d^2)
    // 公式中的 CONSTANT、LINEAR、QUADRATIC 值
    light.Attenuation0 = Attenuation[0];
    light.Attenuation1 = Attenuation[1];
    light.Attenuation2 = Attenuation[2];

    // 光柱圓錐一半的角度 * 2
    light.Phi = DegreeToRadian(SpotlightCutoff*2);

    // Inner Cone的角度, Inner Cone內部沒有角度衰滅.
    light.Theta = DegreeToRadian(0);

    // 光柱的角度衰滅值
    light.Falloff = SpotlightExponent;

    // 光源的 Specular 值
    light.Specular = *(D3DCOLORVALUE*) &Specular;

    // 光源的Ambient值
    light.Ambient = *(D3DCOLORVALUE*) &Ambient;

    // 光源的Diffuse值
    light.Diffuse = *(D3DCOLORVALUE*) &Color;

    // 光源有效範圍, 在此把它設定成浮點數所容計的最大值
    light.Range = 1000.0f;


    // 用SetLight來設定光源屬性
		m_pDEVICE9->SetLight(LightID, &light);
  }

  virtual bool cxxlFASTCALL Exec(const Smart_Ptr<IRenderDatagramDisposalCenterD3d> &IRenderDatagramDisposalCenterD3d_Arg,
    const Smart_Ptr<IRenderDatagram> &IRenderDatagram_Arg, LPDIRECT3DDEVICE9 pDEVICE9)
  {
    m_pDEVICE9 = pDEVICE9;

    Smart_Ptr<ILightSourceMgrRD> ILightSourceMgrRD_Ptr = (ILightSourceMgrRD*)GetDefaultInterface(IRenderDatagram_Arg);

    if((ILightSourceMgrRD*)m_ILightSourceMgrRD_Ptr == (ILightSourceMgrRD*)ILightSourceMgrRD_Ptr)
    {
      if(isModified(m_ILightSourceMgrRD_Ptr) == false)
        return true;
    }
    else
    {
      isModified(ILightSourceMgrRD_Ptr);
      m_ILightSourceMgrRD_Ptr = ILightSourceMgrRD_Ptr;
    }

    Smart_Ptr<ILightAmbient> ILightAmbient_Ptr(NULL);
    ILightSourceMgrRD_Ptr->GetAmbient(ILightAmbient_Ptr);
    if((ILightAmbient*)m_ILightAmbient_Ptr != (ILightAmbient*)ILightAmbient_Ptr)
    {
      m_ILightAmbient_Ptr = ILightAmbient_Ptr;
      C3DVector4 Ambient;
      ILightAmbient_Ptr->GetData(Ambient);

      int r = (int) 255 * Ambient.x;
      int g = (int) 255 * Ambient.y;
      int b = (int) 255 * Ambient.z;
      int a = (int) 255 * Ambient.w;

      D3DCOLOR d3dcolor = D3DCOLOR_ARGB(a, r, g, b);
      // 設定環境光
      m_pDEVICE9->SetRenderState(D3DRS_AMBIENT, d3dcolor);
    }



    Smart_Ptr<cxxlList<ILightSource> > NewILightSource_List = ILightSourceMgrRD_Ptr->GetILightSources();
    Smart_Ptr<cxxlList<ILightSource> > OldILightSource_List = m_ILightSource_List;
    m_ILightSource_List = NewILightSource_List;


    if(OldILightSource_List.isNULL() == false)
    {
      OldILightSource_List->ResetPT(toHead);
      unsigned long OldCount = OldILightSource_List->GetCount();

      // OldILightSource_List 中的元素不存在於 NewILightSource_List 放入這
      Smart_Ptr<cxxlList<ILightSource> > DelILightSource_List = new cxxlList<ILightSource>; 

      unsigned long i = 0;
      for(Smart_Ptr<ILightSource> OldILightSource_Ptr = (*OldILightSource_List)++; i < OldCount; OldILightSource_Ptr = (*OldILightSource_List)++, ++i)
      {
        bool isSame = false; // 有找到改為 true

        // NewILightSource_List 中的元素不存在於 OldILightSource_List 放入這
        Smart_Ptr<cxxlList<ILightSource> > AddILightSource_List = new cxxlList<ILightSource>;

        NewILightSource_List->ResetPT(toHead);
        unsigned long NewCount = NewILightSource_List->GetCount();

        unsigned long j = 0;
        for(Smart_Ptr<ILightSource> NewILightSource_Ptr = (*NewILightSource_List)++; j < NewCount; NewILightSource_Ptr = (*NewILightSource_List)++, ++j)
        {
          if((ILightSource*)OldILightSource_Ptr == (ILightSource*)NewILightSource_Ptr)
          {
            isSame = true;
            while(++j < NewCount)
              AddILightSource_List->Push_Back((*NewILightSource_List)++); // 找到相同之後把其餘都放入
          }
          else
            AddILightSource_List->Push_Back(NewILightSource_Ptr); // 把不相同的放入
        }

        // 沒找到放入待刪 list
        if(isSame == false)
          DelILightSource_List->Push_Back(OldILightSource_Ptr);

        NewILightSource_List = AddILightSource_List; // 準備下一迴圈再找
      }

      DelLightSource(DelILightSource_List);
    }


    D3DXMATRIX vD3DXMATRIX; // 保存用
    m_pDEVICE9->GetTransform(D3DTS_WORLD,&vD3DXMATRIX); // 保存 world matrix
    AddLightSource(NewILightSource_List); 
    m_pDEVICE9->SetTransform(D3DTS_WORLD, &vD3DXMATRIX); // 恢復 world matrix

    return true;
  }

public:

  // Constructor
  CLightSourceMgrDD_D3D9()
    :cxxlObject(Spirit_Easy),
    m_ILightAmbient_Ptr(NULL,this),
    m_ILightSourceMgrRD_Ptr(NULL,this),
    m_ILightSource_List(NULL,this)
  {
    DWORD ID = 0;
    while(ID++ < 8)
      WaitingList.Push_Front(new tagLightSourcMap(ID,(ILightSource*)NULL));
  }

  // Destructor
  virtual ~CLightSourceMgrDD_D3D9()
  {
  }
};

cxxlObject *cxxlFASTCALL New_CLightSourceMgrDD_D3D9()
{
  return new CLightSourceMgrDD_D3D9();
}